package io.shuttle.mbe.api

import io.shuttle.mbe.api.annotation.ChromeMinVersion
import io.shuttle.mbe.api.types.Value1Function
import io.shuttle.mbe.api.types.Value2Function
import io.shuttle.mbe.api.types.Value3Function
import io.shuttle.mbe.api.types.VoidFunction
import io.shuttle.mbe.api.types.Window
import io.shuttle.mbe.core.JSObject

interface Devtools {
    ////////////////////
    // Dev Tools - Inspected Window
    ////////////////////
    /**
     * Use the `Browser.devtools.inspectedWindow` API to interact with the inspected window: obtain the tab ID for the inspected page, evaluate the code in the context of the inspected window, reload the page, or obtain the list of resources within the page.
     *
     * Manifest: "devtools_page"
     */
    interface InspectedWindow {
        // The ID of the tab being inspected. This ID may be used with chrome.tabs.* API.
        var tabId: Number

        // Evaluates a JavaScript expression in the context of the main frame of the inspected page. The expression must evaluate to a JSON-compliant object, otherwise an exception is thrown. The eval function can report either a DevTools-side error or a JavaScript exception that occurs during evaluation. In either case, the result parameter of the callback is undefined. In the case of a DevTools-side error, the isException parameter is non-null and has isError set to true and code set to an error code. In the case of a JavaScript error, isException is set to true and value is set to the string value of thrown object.
        fun eval(
            // An expression to evaluate.
            expression: String,
            options: EvalOptions?,
            callback: Value2Function<Any, EvalExceptionInfo>? = null
        )

        // Retrieves the list of resources from the inspected page.
        fun getResources(callback: Value1Function<List<Devtools.InspectedWindow.Resource>>)

        fun reload(reloadOption: ReloadOptoins?)

        // Fired when a new resource is added to the inspected page.
        val onResourceAdded: Events.Event<Value1Function<Resource>>

        // Fired when a new revision of the resource is committed (e.g. user saves an edited version of the resource in the Developer Tools).
        val onResourceContentCommitted: Events.Event<Value2Function<Resource, String>>

        // A resource within the inspected page, such as a document, a script, or an image.
        data class Resource(
            // The URL of the resource.
            val url: String,
            // Gets the content of the resource.
            // content: Content of the resource (potentially encoded).
            // encoding: Empty if the content is not encoded, encoding name otherwise. Currently, only base64 is supported.
            var getContent: Value2Function<String, String>,
            // Sets the content of the resource.
            // content: New content of the resource. Only resources with the text type are currently supported.
            // commit: True if the user has finished editing the resource, and the new content of the resource should be persisted; false if this is a minor change sent in progress of the user editing the resource.
            // callback.error: Set to undefined if the resource content was set successfully; describes error otherwise.
            var setContent: Value3Function<String, Boolean, Value1Function<Any>>
        )

        data class EvalOptions(
            // If specified, the expression is evaluated on the iframe whose URL matches the one specified. By default, the expression is evaluated in the top frame of the inspected page.
            var frameURL: String?,
            // Evaluate the expression in the context of a content script of an extension that matches the specified origin. If given, scriptExecutionContext overrides the 'true' setting on useContentScriptContext.
            @ChromeMinVersion(107)
            var scriptExecutionContext: String?,
            // Evaluate the expression in the context of the content script of the calling extension, provided that the content script is already injected into the inspected page. If not, the expression is not evaluated and the callback is invoked with the exception parameter set to an object that has the isError field set to true and the code field set to E_NOTFOUND.
            var useContentScriptContext: Boolean?,
        )

        // An object providing details if an exception occurred while evaluating the expression.
        data class EvalExceptionInfo(
            // Set if the error occurred on the DevTools side before the expression is evaluated.
            var code: String,
            // Set if the error occurred on the DevTools side before the expression is evaluated.
            var description: String,
            // Set if the error occurred on the DevTools side before the expression is evaluated, contains the array of the values that may be substituted into the description string to provide more information about the cause of the error.
            var details: List<Any>,
            // Set if the error occurred on the DevTools side before the expression is evaluated.
            var isError: Boolean,
            // Set if the evaluated code produces an unhandled exception.
            var isException: Boolean,
            // Set if the evaluated code produces an unhandled exception.
            var value: String,
        )

        data class ReloadOptoins(
            // When true, the loader will bypass the cache for all inspected page resources loaded before the load event is fired. The effect is similar to pressing Ctrl+Shift+R in the inspected window or within the Developer Tools window.
            var ignoreCache: Boolean?,
            // If specified, the script will be injected into every frame of the inspected page immediately upon load, before any of the frame's scripts. The script will not be injected after subsequent reloads—for example, if the user presses Ctrl+R.
            var injectedScript: String?,
            // If specified, the string will override the value of the User-Agent HTTP header that's sent while loading the resources of the inspected page. The string will also override the value of the navigator.userAgent property that's returned to any scripts that are running within the inspected page.
            var userAgent: String?,
        )
    }

    interface Network {
        // Returns HAR log that contains all known network requests.
        // harLog: A HAR log. See HAR specification for details.
        fun getHAR(callback: Value1Function<JSObject>)

        // Fired when the inspected window navigates to a new page.
        val onNavigated: Events.Event<Value1Function<String>>

        // Fired when a network request is finished and all request data are available.
        val onRequestFinished: Events.Event<Value1Function<Request>>

        // Represents a network request for a document resource (script, image and so on). See HAR Specification for reference.
        data class Request(
            // Returns content of the response body.
            // content: Content of the response body (potentially encoded).
            // encoding: Empty if the content is not encoded, encoding name otherwise. Currently, only base64 is supported.
            var getContent: Value2Function<String, String>
        )
    }

    interface Panels {

        var elements: ElementsPanel

        var sources: SourcesPanel

        // The name of the color theme set in user's DevTools settings. Possible values: default (the default) and dark.
        @ChromeMinVersion(59)
        var themeName: String

        // Creates an extension panel.
        fun create(
            // Title that is displayed next to the extension icon in the Developer Tools toolbar.
            title: String,
            // Path of the panel's icon relative to the extension directory.
            iconPath: String,
            // Path of the panel's HTML page relative to the extension directory.
            pagePath: String,
            // @param {callback.panel} - An ExtensionPanel object representing the created panel.
            callback: Value1Function<ExtensionPanel>? = null
        )

        // Requests DevTools to open a URL in a Developer Tools panel.
        fun openResource(
            // The URL of the resource to open.
            url: String,
            // Specifies the line number to scroll to when the resource is loaded.
            lineNumber: Number,
            // Specifies the column number to scroll to when the resource is loaded.
            columnNumber: Number?,
            callback: VoidFunction? = null
        )

        // Specifies the function to be called when the user clicks a resource link in the Developer Tools window.
        // To unset the handler, either call the method with no parameters or pass null as the parameter.
        fun setOpenResourceHandler(
            // @param {callback.resource} - A devtools.inspectedWindow.Resource object for the resource that was clicked.
            // @param {callback.lineNumber} - Specifies the line number within the resource that was clicked.
            callback: Value2Function<InspectedWindow.Resource, Number>? = null
        )

        // A button created by the extension.
        data class Button(
            // Fired when the button is clicked.
            var onClicked: Events.Event<VoidFunction>,
            // Updates the attributes of the button. If some of the arguments are omitted or null, the corresponding attributes are not updated.
            var update: Events.Event<Value3Function<String?, String?, Boolean?>>
        )

        // Represents the Elements panel.
        data class ElementsPanel(
            // Fired when an object is selected in the panel.
            var onSelectionChanged: Events.Event<VoidFunction>,
            // Creates a pane within panel's sidebar.
            var createSidebarPane: Events.Event<Value2Function<String, Value1Function<Any>>>
        )

        // Represents a panel created by an extension.
        data class ExtensionPanel(
            // Fired when the user switches away from the panel.
            var onHidden: VoidFunction,
            // Fired upon a search action (start of a new search, search result navigation, or search being canceled).
            var onSearch: Value2Function<String, String?>,
            // Fired when the user switches to the panel.
            var onShown: Value1Function<Window>,
            // Appends a button to the status bar of the panel.
            // @return {iconPath} - Path to the icon of the button. The file should contain a 64x24-pixel image composed of two 32x24 icons.
            //  The left icon is used when the button is inactive; the right icon is displayed when the button is pressed.
            // @return {tooltipText} - Text shown as a tooltip when user hovers the mouse over the button.
            // @return {disabled} - Whether the button is disabled.
            var createStatusBarButton: Value3Function<String, String, Boolean>,
            // Shows the panel by activating the corresponding tab.
            var show: VoidFunction
        )

        // A sidebar created by the extension.
        data class ExtensionSidebarPane(
            // Fired when the sidebar pane becomes hidden as a result of the user switching away from the panel that hosts the sidebar pane.
            var onHidden: VoidFunction,
            // Fired when the sidebar pane becomes visible as a result of user switching to the panel that hosts it.
            var onShown: Value1Function<Value1Function<Window>>,
            // Sets an expression that is evaluated within the inspected page. The result is displayed in the sidebar pane.
            var setExpression: Value3Function<String, String?, VoidFunction>,
            // @param {height} - A CSS-like size specification, such as '100px' or '12ex'.
            var setHeight: Value1Function<String>,
            // Sets a JSON-compliant object to be displayed in the sidebar pane.
            // @param {jsonObject} - An object to be displayed in context of the inspected page. Evaluated in the context of the caller (API client).
            // @param {rootTitle} - An optional title for the root of the expression tree.
            // @param {callback}
            var setObject: Value3Function<String, String?, VoidFunction>,
            // Sets an HTML page to be displayed in the sidebar pane.
            // @param {path} - Relative path of an extension page to display within the sidebar.
            var setPage: Value1Function<String>,
        )

        // Represents the Sources panel.
        data class SourcesPanel(
            // Fired when an object is selected in the panel.
            var onSelectedSourceChanged: Value1Function<VoidFunction>,
            // Creates a pane within panel's sidebar.
            // @param {title} - Text that is displayed in sidebar caption.
            // @param {callback.result} - An ExtensionSidebarPane object for created sidebar pane.
            var createSidebarPane: Value2Function<String, Value1Function<ExtensionSidebarPane>>
        )

    }

    // FIMXE: lackof devtools.performance's documents
    interface Recorder {

        // Creates a view that can handle the replay. This view will be embedded inside the Recorder panel.
        @ChromeMinVersion(112)
        fun createView(
            // Title that is displayed next to the extension icon in the Developer Tools toolbar.
            title: String,
            // Path of the panel's HTML page relative to the extension directory.
            pagePath: String
        ): RecorderView

        fun registerRecorderExtensionPlugin(
            // An instance implementing the RecorderExtensionPlugin interface.
            plugin: RecorderExtensionPlugin,
            // The name of the plugin.
            name: String,
            // The media type of the string content that the plugin produces.
            mediaType: String
        )

        // A plugin interface that the Recorder panel invokes to customize the Recorder panel.
        data class RecorderExtensionPlugin(
            // Allows the extension to implement custom replay functionality.
            // @param {replay.recording} - A recording of the user interaction with the page. This should match Puppeteer's recording schema.
            @ChromeMinVersion(112)
            var replay: Value1Function<JSObject>,
            // Converts a recording from the Recorder panel format into a string.
            // @param {stringify.recording} - A recording of the user interaction with the page. This should match Puppeteer's recording schema.
            var stringify: Value1Function<JSObject>,
            // Converts a step of the recording from the Recorder panel format into a string.
            // @param {stringifyStep.step} - A step of the recording of a user interaction with the page. This should match Puppeteer's step schema.
            var stringifyStep: Value1Function<JSObject>,
        )

        // Represents a view created by extension to be embedded inside the Recorder panel.
        @ChromeMinVersion(112)
        data class RecorderView(
            // Fired when the view is hidden.
            var onHidden: VoidFunction,
            // Fired when the view is shown.
            var onShown: VoidFunction,
            // Indicates that the extension wants to show this view in the Recorder panel.
            var show: VoidFunction,
        )
    }
}